﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Sinacor.Infra.UI.Authorization.Infra.Authorization.Authorization;
using Sinacor.Infra.UI.Security.Services;
using Sinacor.Infra.UI.Common;
using Sinacor.Infra.UI.Session;
using System.Configuration;
using System.Xml.Linq;
using PSE.Framework.Common.Security.Services.Configuration;

namespace Sinacor.Infra.UI.Authorization
{
    /// <summary>
    /// Classe responsável por recuperar informações de autorização do usuário.
    /// </summary>
    public class AuthorizationManager
    {
        private static AuthorizationManager _instance;
        private static Mutex _mutex = new Mutex();

        private Dictionary<string, List<string>> _rolesByModule = new Dictionary<string, List<string>>();

        /// <summary>
        /// Construtor privado para impedir mais de uma instância da classe.
        /// </summary>
        private AuthorizationManager()
        {
        }

        /// <summary>
        /// Retorna uma instância da classe AuthorizationManager
        /// </summary>
        public static AuthorizationManager GetInstance()
        {
            _mutex.WaitOne();
            if (_instance == null)
                _instance = new AuthorizationManager();
            _mutex.ReleaseMutex();

            return _instance;
        }

        #region RetrieveUserRoles / CheckUserAccess

        /// <summary>
        /// Retorna a lista de funções que o usuário tem acesso.
        /// </summary>
        /// <returns></returns>
        public List<string> RetrieveUserRoles()
        {
            List<string> ret = new List<string>();

            string configPath = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;

            XDocument document = XDocument.Load(configPath);

            var modules = from m in document.Descendants()
                          where m.Name.LocalName == "module"
                          select m;

            foreach (XElement module in modules)
            {
                if (module.Attribute("name") != null)
                {
                    ret.AddRange(RetrieveUserRoles(module.Attribute("name").Value));
                }
            }

            ret = ret.Distinct().ToList();
            ret.Sort();

            return ret;
        }

        /// <summary>
        /// Retorna a lista de funções que o usuário tem acesso.
        /// </summary>
        /// <param name="moduleId"></param>
        /// <returns></returns>
        public List<string> RetrieveUserRoles(string moduleId)
        {
            Session.Session session = SessionManager.GetInstance().GetCurrentSession();

            if (session == null)
                throw new InvalidOperationException("This method could not be called without a session.");

            string id = session.Company.Id + "_" + moduleId;

            List<string> roles = null;

            if (!_rolesByModule.ContainsKey(id))
            {
                roles = RetrieveUserRolesFromService(moduleId, session);
                //Adiciona a lista de roles no dicionário.
                _rolesByModule.Add(id, roles);
            }
            else
                roles = _rolesByModule[id];

            return roles;
        }

        /// <summary>
        /// Consome o serviço e retorna a lista de módulos deste.
        /// </summary>
        /// <param name="moduleId">Id do módulo</param>
        /// <returns>Lista com as roles do usuário/empresa/módulo</returns>
        private List<string> RetrieveUserRolesFromService(string moduleId, Session.Session session)
        {
            List<string> roles = new List<string>();

            SessionInformation sessionInformation = session.GenerateSessionInformation();

            try
            {
                List<UserRolesResponse> response;
                using (ServiceInstance<IAuthorization> si = new ServiceInstance<IAuthorization>())
                {
                    IAuthorization service = si.TryGetInstance(sessionInformation, CriptographyLevelType.Federated);

                    RetrieveUserRolesRequest request = new RetrieveUserRolesRequest();
                    request.ModuleId = moduleId;
                    response = service.RetrieveUserRoles(request);
                }

                foreach (UserRolesResponse userRoles in response)
                {
                    roles.Add(userRoles.Role);
                }

            }
            catch (Exception ex)
            {
                throw new Exception("An error occurred trying to retrieve user roles. See inner exception for more details.", ex);
            }

            return roles;
        }

        /// <summary>
        /// Verifica se o usuário tem acesso a uma determinada função.
        /// </summary>
        /// <param name="moduleId">Id do Modulo</param>
        /// <param name="role">função</param>
        /// <returns></returns>
        public bool CheckUserAccess(string moduleId, string role)
        {
            foreach (string r in RetrieveUserRoles(moduleId))
                if (role == r)
                    return true;
            return false;
        }

        #endregion

        #region VerifyInstalledModuleIds

        /// <summary>
        /// Verifies the installed module id from service.
        /// </summary>
        /// <param name="moduleId">The module id.</param>
        /// <returns></returns>
        private Boolean VerifyInstalledModuleIdFromService(String moduleId, Session.Session session)
        {
            SessionInformation sessionInformation = session.GenerateSessionInformation();

            try
            {
                using (ServiceInstance<IAuthorization> si = new ServiceInstance<IAuthorization>())
                {
                    IAuthorization service = si.TryGetInstance(sessionInformation, CriptographyLevelType.Federated);

                    return service.VerifyInstalledModuleId(moduleId);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("An error occurred trying to verify installed ModuleId. See inner exception for more details.", ex);
            }
        }

        /// <summary>
        /// Verifies the installed module id.
        /// </summary>
        /// <param name="moduleId">The module id.</param>
        /// <returns></returns>
        public Boolean VerifyInstalledModuleId(string moduleId)
        {
            Session.Session session = SessionManager.GetInstance().GetCurrentSession();

            if (session == null)
                throw new InvalidOperationException("This method could not be called without a session.");

            if (String.IsNullOrEmpty(moduleId))
                throw new InvalidOperationException("This method could not be called without a moduleId.");

            return this.VerifyInstalledModuleIdFromService(moduleId, session);
        }

        #endregion

    }
}
